package com.dlc.shop.search.common.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.dlc.shop.bean.bo.ProductBO;
import com.dlc.shop.bean.enums.EsOperationType;
import com.dlc.shop.bean.enums.ProdStatusEnums;
import com.dlc.shop.bean.enums.ProdType;
import com.dlc.shop.bean.event.EsProductEvent;
import com.dlc.shop.bean.event.EsProductUpdateEvent;
import com.dlc.shop.bean.model.Product;
import com.dlc.shop.bean.vo.EsProdUpdateVO;
import com.dlc.shop.dao.ProductMapper;
import com.dlc.shop.search.common.service.EsProductService;
import com.dlc.shop.search.common.util.EsSearchUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Yami
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class EsProductServiceImpl implements EsProductService {

    private final ApplicationEventPublisher eventPublisher;
    private final ProductMapper productMapper;

    @Override
    public void save(Long prodId) {
        ProductBO productBO = getProductBo(prodId);
        if (Objects.equals(productBO.getStatus(), ProdStatusEnums.DELETE.getValue())) {
            return;
        }
        EsSearchUtil.save(productBO.getProdId(), productBO);
    }

    @Override
    public void saveBatch(List<Long> prodIds) {
        List<ProductBO> productBOList = getProductBo(prodIds);
        if (CollUtil.isEmpty(productBOList)) {
            return;
        }
        Map<Long, ProductBO> map = productBOList.stream()
                .filter(productBO -> !Objects.equals(productBO.getStatus(), ProdStatusEnums.DELETE.getValue()))
                .collect(Collectors.toMap(ProductBO::getProdId, p -> p));
        EsSearchUtil.saveBatch(map);
    }

    @Override
    public void update(Long prodId) {
        if (Objects.isNull(prodId)) {
            return;
        }
        ProductBO productBO = getProductBo(prodId);
        EsSearchUtil.update(productBO);
    }

    @Override
    public void updateBatch(List<Long> prodIds) {
        if (CollUtil.isEmpty(prodIds)) {
            return;
        }
        List<ProductBO> productBOList = getProductBo(prodIds);
        Map<Long, ProductBO> map = new HashMap<>(productBOList.size());
        for (ProductBO productBO : productBOList) {
            map.put(productBO.getProdId(), productBO);
        }
        EsSearchUtil.updateBatch(map);
    }

    @Override
    public void updateProdComm(Long prodId) {
        if (Objects.isNull(prodId)) {
            return;
        }
        ProductBO productBO = getProductBo(prodId, EsOperationType.UPDATE_PROD_COMM);
        Script script = new Script(ScriptType.INLINE,
                "painless",
                "ctx._source.commentNum =" + productBO.getCommentNum(),
                Collections.emptyMap());

        Map<Long, Script> prodMap = new HashMap<>(1);
        prodMap.put(productBO.getProdId(), script);
        EsSearchUtil.updateStockByPainless(prodMap);
    }

    @Override
    public void updateByCategoryId(Long categoryId) {
        if (Objects.isNull(categoryId)) {
            return;
        }
        List<Long> ids = productMapper.listProdId(categoryId, null, null);
        updateBatch(ids);
    }

    @Override
    public void updateByShopCategoryId(Long shopCategoryId) {
        if (Objects.isNull(shopCategoryId)) {
            return;
        }
        List<Long> ids = productMapper.listProdId(null, shopCategoryId, null);
        updateBatch(ids);
    }

    @Override
    public void updateSoldNum(Long prodId) {
        if (Objects.isNull(prodId)) {
            return ;
        }
        ProductBO productBO = getProductBo(prodId, EsOperationType.UPDATE_SOLD_NUM);
        Map<Long, Script> map = new HashMap<>(1);
        Script script = new Script(ScriptType.INLINE,
                "painless",
                "ctx._source.totalStocks = " + productBO.getTotalStocks() + ";" +
                        "ctx._source.soldNum = " + productBO.getSoldNum() + ";" +
                        "ctx._source.waterSoldNum= " + productBO.getWaterSoldNum() + ";",
                Collections.emptyMap());
        map.put(productBO.getProdId(), script);
        EsSearchUtil.updateStockByPainless(map);
    }

    @Override
    public void updateSoldNumBatch(List<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return ;
        }
        Map<Long, Script> map = new HashMap<>(16);
        List<ProductBO> productList = getProductBo(ids, EsOperationType.UPDATE_SOLD_NUM);
        for (ProductBO productBO : productList) {
            Script script = new Script(ScriptType.INLINE,
                    "painless",
                    "ctx._source.totalStocks = " + productBO.getTotalStocks() + ";" +
                            "ctx._source.soldNum = " + productBO.getSoldNum() + ";",
                    Collections.emptyMap());
            map.put(productBO.getProdId(), script);
        }
        EsSearchUtil.updateStockByPainless(map);
    }

    @Override
    public void updateByShopId(Long shopId) {
        if (Objects.isNull(shopId)) {
            return;
        }
        List<Long> ids = productMapper.listProdId(null, null, shopId);
        updateBatch(ids);
    }

    @Override
    public void delete(Long prodId) {
        if (Objects.isNull(prodId)) {
            return;
        }
        EsSearchUtil.delete(prodId);
    }

    @Override
    public void deleteBatch(List<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        EsSearchUtil.deleteBatch(ids);
    }

    @Override
    public void updateByGroupId(Long id) {
        if (Objects.isNull(id)) {
            return;
        }
        List<Product> products = productMapper.selectList(new LambdaQueryWrapper<Product>()
                .eq(Product::getProdType, ProdType.PROD_TYPE_GROUP.value())
                .eq(Product::getActivityId, id));
        List<Long> prodIds = products.stream().map(Product::getProdId).collect(Collectors.toList());
        updateBatch(prodIds);
    }

    @Override
    public void changeProdStockBatch(List<EsProdUpdateVO> prodList) {
        if (CollUtil.isEmpty(prodList)) {
            return;
        }
        Map<Long, Script> map = new HashMap<>(prodList.size());
        for (EsProdUpdateVO esProdUpdateVO : prodList) {
            String code = "-= ";
            if (Objects.equals(esProdUpdateVO.getType(), 1)) {
                code = "+= ";
            }
            Script script = new Script(ScriptType.INLINE,
                    "painless",
                    "ctx._source.totalStocks " + code + esProdUpdateVO.getCount(),
                    Collections.emptyMap());
            map.put(esProdUpdateVO.getProdId(), script);
        }
        EsSearchUtil.updateStockByPainless(map);
    }

    @Override
    public void changeProdSoldBatch(List<EsProdUpdateVO> prodList) {
        if (CollUtil.isEmpty(prodList)) {
            return;
        }
        Map<Long, Script> map = new HashMap<>(prodList.size());
        for (EsProdUpdateVO esProdUpdateVO : prodList) {
            String code = "-= ";
            if (Objects.equals(esProdUpdateVO.getType(), 1)) {
                code = "+= ";
            }
            code = "ctx._source.soldNum " + code + esProdUpdateVO.getCount() + ";" + "ctx._source.actualSoldNum " + code + esProdUpdateVO.getCount();
            Script script = new Script(ScriptType.INLINE,
                    "painless",
                    code,
                    Collections.emptyMap());
            map.put(esProdUpdateVO.getProdId(), script);
        }
        EsSearchUtil.updateStockByPainless(map);
    }

    @Override
    public List<Long> listProdIdByEvent(EsProductUpdateEvent event) {
        switch (event.getEsOperationType()) {
            case SAVE, UPDATE, DELETE, UPDATE_SOLD_NUM, UPDATE_PROD_COMM -> {
                return Collections.singletonList(event.getId());
            }
            case SAVE_BATCH, UPDATE_BATCH, DELETE_BATCH, UPDATE_SOLD_NUM_BATCH -> {
                return event.getIds();
            }
            case UPDATE_ORDER_STOCK_NUM_BATCH, UPDATE_ORDER_SOLD_NUM_BATCH -> {
                if (CollectionUtils.isEmpty(event.getProdList())) {
                    return Collections.emptyList();
                }
                return event.getProdList().stream().map(EsProdUpdateVO::getProdId).toList();
            }
            case UPDATE_BY_CATEGORY_ID -> {
                return productMapper.listProdId(event.getId(), null, null);
            }
            case UPDATE_BY_SHOP_CATEGORY_ID -> {
                return productMapper.listProdId(null, event.getId(), null);
            }
            case UPDATE_BY_SHOP_ID -> {
                return productMapper.listProdId(null, null, event.getId());
            }
            case UPDATE_BY_GROUP_ID -> {
                List<Product> products = productMapper.selectList(new LambdaQueryWrapper<Product>()
                        .eq(Product::getProdType, ProdType.PROD_TYPE_GROUP.value())
                        .eq(Product::getActivityId, event.getId()));
                if (CollectionUtils.isEmpty(products)) {
                    return Collections.emptyList();
                }
                return products.stream().map(Product::getProdId).toList();
            }
            default -> {
                return Collections.emptyList();
            }
        }
    }

    /**
     * 获取商品信息
     * @param prodId 商品id
     * @return
     */
    private ProductBO getProductBo(Long prodId) {
        if (Objects.isNull(prodId)) {
            return null;
        }
        List<ProductBO> productList = getProductBo(Collections.singletonList(prodId));
        if (CollUtil.isEmpty(productList)) {
            return null;
        }
        return productList.get(0);
    }

    /**
     * 获取商品信息
     * @param prodId 商品id
     * @return
     */
    private ProductBO getProductBo(Long prodId, EsOperationType operationType) {
        if (Objects.isNull(prodId)) {
            return null;
        }
        List<ProductBO> productList = getProductBo(Collections.singletonList(prodId), operationType);
        if (CollUtil.isEmpty(productList)) {
            return null;
        }
        return productList.get(0);
    }

    /**
     * 获取商品信息
     * @param prodIds 商品id
     * @return
     */
    private List<ProductBO> getProductBo(List<Long> prodIds) {
        return listProductBo(prodIds, null);
    }

    /**
     * 获取商品信息
     * @param prodIds 商品id
     * @return
     */
    private List<ProductBO> getProductBo(List<Long> prodIds, EsOperationType operationType) {
        return listProductBo(prodIds, operationType);
    }

    /**
     * 获取商品信息
     * @param prodIds 商品id列表
     * @return
     */
    private List<ProductBO> listProductBo(List<Long> prodIds, EsOperationType operationType) {
        if (CollUtil.isEmpty(prodIds)) {
            return null;
        }
        List<ProductBO> productList = new ArrayList<>();
        try {
            eventPublisher.publishEvent(new EsProductEvent(prodIds, productList, operationType));
        } catch (Exception e) {
            log.error("获取es商品数据异常：", e);
        }
        if (CollUtil.isEmpty(productList) || Objects.isNull(productList.get(0))) {
            log.error("商品id为：{}, type:{}的商品数据异常！", prodIds, operationType.value());
            return null;
        }
        return productList;
    }
}
